import Callout from '@/components/Callout';
import PartnerContentLink from '@/components/PartnerContentLink';
import Details from '@/components/Details';
import {CourseVideoBanner} from '@/components/CourseBanner';

# Rendering translations

Prefer to watch a video?

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/03-translations/01-setup"
  title="First translations"
/>

## Terminology

- **Locale**: We use this term to describe an identifier that contains the language and formatting preferences of users. Apart from the language, a locale can include optional regional information (e.g. `en-US`).
- **Messages**: These are collections of key/translation pairs that are grouped by locale (e.g. `en-US.json`).

## Structuring messages

Messages are typically defined in JSON files:

```json filename="en.json"
{
  "About": {
    "title": "About us"
  }
}
```

You can render messages from within a React component with the `useTranslations` hook:

```tsx filename="About.tsx"
import {useTranslations} from 'next-intl';

function About() {
  const t = useTranslations('About');
  return <h1>{t('title')}</h1>;
}
```

To retrieve all available messages in a component, you can omit the namespace path:

```js
const t = useTranslations();

t('About.title');
```

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/03-translations/03-keys"
  title="Organizing keys"
/>

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/03-translations/04-message-reuse"
  title="Reusing messages"
/>

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/03-translations/05-tooling"
  title="Editor tools"
/>

<Callout>

Translators can collaborate on messages by using a localization management solution like <PartnerContentLink name="messages-collaborate" href="https://crowdin.com/teams/engineering">Crowdin</PartnerContentLink>.

</Callout>

<Details id="messages-nested-structure">
<summary>How can I provide more structure for messages?</summary>

Optionally, you can structure your messages as nested objects.

```json filename="en.json"
{
  "auth": {
    "SignUp": {
      "title": "Sign up",
      "form": {
        "placeholder": "Please enter your name",
        "submit": "Submit"
      }
    }
  }
}
```

```tsx filename="SignUp.tsx"
import {useTranslations} from 'next-intl';

function SignUp() {
  // Provide the lowest common denominator that contains
  // all messages this component needs to consume.
  const t = useTranslations('auth.SignUp');

  return (
    <>
      <h1>{t('title')}</h1>
      <form>
        <input
          // The remaining hierarchy can be resolved by
          // using `.` to access nested messages.
          placeholder={t('form.placeholder')}
        />
        <button type="submit">{t('form.submit')}</button>
      </form>
    </>
  );
}
```

</Details>

<Details id="messages-outside-components">
<summary>How can I use translations outside of components?</summary>

`next-intl` is heavily based on the `useTranslations` API which is intended to consume translations from within React components. This may seem like a limitation, but this is intentional and aims to encourage the use of proven patterns that avoid potential issues that are easy to overlook.

There's one exception however: [Using `next-intl` with Server Actions, Metadata and Route Handlers](/docs/environments/actions-metadata-route-handlers).

If you're interested to dive deeper into this topic, there's a blog post that discusses the background of this design decision: [How (not) to use translations outside of React components](/blog/translations-outside-of-react-components).

</Details>

<Details id="messages-other-styles">
<summary>Can I use a different style for structuring my messages?</summary>

Namespace keys can not contain the character "." as this is used to express nesting—all other characters are fine to use.

If you're migrating from a flat structure that uses "." in message keys, you can convert your messages to a nested structure as follows:

```tsx
import {set} from 'lodash';

const input = {
  'one.one': '1.1',
  'one.two': '1.2',
  'two.one.one': '2.1.1'
};

const output = Object.entries(input).reduce(
  (acc, [key, value]) => set(acc, key, value),
  {}
);
```

**Output:**

```tsx
{
  "one": {
    "one": "1.1",
    "two": "1.2"
  },
  "two": {
    "one": {
      "one": "2.1.1"
    }
  }
}
```

This keeps the hierarchy while removing the redundancy of repeated parent keys. You can either execute this is in a one-off script or before passing your messages to `next-intl`.

If you've previously used human readable sentences for keys, you can theoretically map the `.` character to a different one (e.g. `_`) before passing the messages to `next-intl`, but it's generally recommended to use IDs as keys. You can still view human readable labels from your messages in your editor by using a [VSCode integration](/docs/workflows/vscode-integration) in case that's your primary motivation for using human readable keys.

</Details>

## ICU messages

`next-intl` uses [ICU message syntax](https://unicode-org.github.io/icu/userguide/format_parse/messages/) that allows you to express language nuances and separates state handling within messages from your app code.

<CourseVideoBanner
  className="mt-4"
  href="https://learn.next-intl.dev/chapters/03-translations/06-icu-messages"
  title="ICU messages"
/>

### Static messages

Static messages will be used as-is:

```json filename="en.json"
"message": "Hello world!"
```

```js
t('message'); // "Hello world!"
```

### Interpolation of arguments [#arguments]

Dynamic values can be inserted into messages by using curly braces:

```json filename="en.json"
"message": "Hello {name}!"
```

```js
t('message', {name: 'Jane'}); // "Hello Jane!"
```

<Details id="interpolation-supported-characters">
<summary>Which characters are supported for value names?</summary>

Value names are required to be alphanumeric and can contain underscores. All other characters, including dashes, are not supported.

</Details>

### Cardinal pluralization

To express the pluralization of a given number of items, the `plural` argument can be used:

```json filename="en.json"
"message": "You have {count, plural, =0 {no followers yet} =1 {one follower} other {# followers}}."
```

```js
t('message', {count: 3580}); // "You have 3,580 followers."
```

Note that by using the `#` marker, the value will be [formatted as a number](/docs/usage/numbers).

<Details id="cardinal-plural-forms">
<summary>Which plural forms are supported?</summary>

Languages have different rules for pluralization and your messages should reflect these rules.

For example, English has two forms: One for the singular (e.g. "1 _follower_") and one for everything else (e.g. "0 _followers_" and "2 _followers_"). In contrast, Chinese only has one form, while Arabic has six.

The other aspect to consider is that from a usability perspective, it can be helpful to consider additional cases on top—most commonly the special case for zero (e.g. "No followers yet" instead of "0 followers").

While you can always use the `=value` syntax to match a specific number (e.g. `=0`), you can choose from the following tags depending on what grammar rules apply to a given language:

- `zero`: For languages with zero-item grammar (e.g., Latvian, Welsh).
- `one`: For languages with singular-item grammar (e.g., English, German).
- `two`: For languages with dual-item grammar (e.g., Arabic, Welsh).
- `few`: For languages with grammar specific to a small number of items (e.g., Arabic, Croatian).
- `many`: For languages with grammar specific to a larger number of items (e.g., Arabic, Croatian).
- `other`: Used when the value doesn't match other plural categories.

`next-intl` uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to detect the correct tag that should be used for a given number. The exact range that `few` and `many` applies to varies depending on the locale (see the [language plural rules](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html) table in the Unicode CLDR).

</Details>

### Ordinal pluralization

To apply pluralization based on an order of items, the `selectordinal` argument can be used:

```tsx filename="en.json"
"message": "It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!"
```

<Details id="ordinal-plural-forms">
<summary>Which plural forms are supported?</summary>

Depending on the language, different forms of ordinal pluralization are supported.

For example, English has four forms: "th", "st", "nd" and "rd" (e.g. 1st, 2nd, 3rd, 4th … 11th, 12th, 13th … 21st, 22nd, 23rd, 24th, etc.). In contrast, both Chinese and Arabic use only one form for ordinal numbers.

`next-intl` uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to detect the correct _tag_ that should be used for a given number.

**These tags are supported:**

- `zero`: For languages with zero-item grammar (e.g., Latvian, Welsh).
- `one`: For languages with singular-item grammar (e.g., English, German).
- `two`: For languages with dual-item grammar (e.g., Arabic, Welsh).
- `few`: For languages with grammar specific to a small number of items (e.g., Arabic, Polish, Croatian).
- `many`: For languages with grammar specific to a larger number of items (e.g., Arabic, Polish, Croatian).
- `other`: Used when the value doesn't match other plural categories.

The exact range that `few` and `many` applies to varies depending on the locale (see the [language plural rules](https://www.unicode.org/cldr/charts/43/supplemental/language_plural_rules.html) table in the Unicode CLDR).

To match a specific number, `next-intl` additionally supports the special `=value` syntax (e.g. `=3`) that always takes precedence.

</Details>

### Selecting enum-based values

To map identifiers to human readable labels, you can use the `select` argument that works similar to the `switch` statement in JavaScript:

```tsx filename="en.json"
"message": "{gender, select, female {She is} male {He is} other {They are}} online."
```

```js
t('message', {gender: 'female'}); // "She is online."
```

**Note**: The `other` case is required and will be used when none of the specific values match.

<Details id="select-values-supported-characters">
<summary>Which characters are supported for select values?</summary>

Values are required to be alphanumeric and can contain underscores. All other characters, including dashes, are not supported.

Therefore, e.g. when you're mapping a locale to a human readable string, you should map the dash to an underscore first:

```tsx filename="en.json"
"label": "{locale, select, en_GB {British English} en_US {American English} other {Unknown}}"
```

```tsx
const locale = 'en-GB';
t('message', {locale: locale.replaceAll('-', '_')});
```

</Details>

### Escaping

Since curly braces are used for [interpolating arguments](/docs/usage/translations#arguments), you can wrap them in single quotes (`'`) to use the actual symbol in messages:

```json filename="en.json"
"message": "Escape curly braces with single quotes (e.g. '{name}')"
```

```js
t('message'); // "Escape curly braces with single quotes (e.g. {name})"
```

## Rich text

You can format rich text with custom tags and map them to React components via `t.rich`:

```json filename="en.json"
{
  "message": "Please refer to <guidelines>the guidelines</guidelines>."
}
```

```js
// Returns `<>Please refer to <a href="/guidelines">the guidelines</a>.</>`
t.rich('message', {
  guidelines: (chunks) => <a href="/guidelines">{chunks}</a>
});
```

Tags can be arbitrarily nested (e.g. `This is <important><very>very</very> important</important>`).

<Details id="rich-text-reuse-tags">
<summary>How can I reuse tags across my app?</summary>

Common tags for rich text that you want to share across your app can be defined in a shared module and imported where relevant for usage with `t.rich`.

A convenient pattern is to use a component that provides common tags via a render prop:

```js
import {useTranslations} from 'next-intl';
import RichText from '@/components/RichText';

function AboutPage() {
  const t = useTranslations('AboutPage');
  return <RichText>{(tags) => t.rich('description', tags)}</RichText>;
}
```

In this case, the `RichText` component can provide styled tags and also a general layout for the text:

```js filename="components/RichText.tsx"
import {ReactNode} from 'react';

// These tags are available
type Tag = 'p' | 'b' | 'i';

type Props = {
  children(tags: Record<Tag, (chunks: ReactNode) => ReactNode>): ReactNode
};

export default function RichText({children}: Props) {
  return (
    <div className="prose">
      {children({
        p: (chunks: ReactNode) => <p>{chunks}</p>,
        b: (chunks: ReactNode) => <b className="font-semibold">{chunks}</b>,
        i: (chunks: ReactNode) => <i className="italic">{chunks}</i>
      })}
    </div>
  );
}
```

If you need to combine shared tags with values from your component, you can merge them accordingly by using the spread operator:

```js
function UserPage({username}) {
  const t = useTranslations('UserPage');
  return (
    <RichText>{(tags) => t.rich('description', {...tags, username})}</RichText>
  );
}
```

</Details>

<Details id="rich-text-self-closing">
<summary>How can I use "self-closing" tags without any chunks?</summary>

Messages can use tags without any chunks as children, but syntax-wise, a closing tag is required by the ICU parser:

```json filename="en.json"
{
  "message": "Hello,<br></br>how are you?"
}
```

```js
t.rich('message', {
  br: () => <br />
});
```

</Details>

<Details id="rich-text-attributes">
<summary>How can I pass attributes to tags?</summary>

Attributes can only be set on the call site, not within messages:

```json filename="en.json"
{
  "message": "Go to <profile>my profile</profile>"
}
```

```js
t.rich('message', {
  profile: (chunks) => <Link href="/profile">{chunks}</Link>
});
```

In case you have attribute values that are required to be configured as part of your messages, you can retrieve them from a separate message and pass them to another one as an attribute:

```json filename="en.json"
{
  "message": "See this <partner>partner website</partner>.",
  "partnerHref": "https://partner.example.com"
}
```

```js
t.rich('message', {
  partner: (chunks) => <a href={t('partnerHref')}>{chunks}</a>
});
```

For the use case of localizing pathnames, consider using [`pathnames`](/docs/routing/configuration#pathnames).

</Details>

## HTML markup

To render rich text, you typically want to use [rich text formatting](#rich-text). However, if you have a use case where you need to emit raw HTML markup, you can use the `t.markup` function:

```json filename="en.json"
{
  "markup": "This is <important>important</important>"
}
```

```js
// Returns 'This is <b>important</b>'
t.markup('markup', {
  important: (chunks) => `<b>${chunks}</b>`
});
```

Note that unlike `t.rich`, the provided markup functions accept `chunks` as a `string` and also return a `string` where the `chunks` are wrapped accordingly.

## Raw messages

Messages are always parsed and therefore e.g. for rich text formatting you need to supply the necessary tags. If you want to avoid the parsing, e.g. because you have raw HTML stored in a message, there's a separate API for this use case:

```json filename="en.json"
{
  "content": "<h1>Headline</h1><p>This is raw HTML</p>"
}
```

```js
<div dangerouslySetInnerHTML={{__html: t.raw('content')}} />
```

<Callout type="warning">
  **Important**: You should always sanitize the content that you pass to
  [`dangerouslySetInnerHTML`](https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html)
  to avoid cross-site scripting attacks.
</Callout>

The value of a raw message can be any valid JSON value: strings, booleans, objects and arrays.

## Optional messages [#t-has]

If you have messages that are only available for certain locales, you can use the `t.has` function to check whether a message is available for the current locale:

```js
const t = useTranslations('About');

t.has('title'); // true
t.has('unknown'); // false
```

Note that separately from this, you can also provide [fallback messages](/docs/usage/configuration#messages), e.g. from the default locale, in case you have incomplete messages for certain locales.

## Arrays of messages

If you need to render a list of messages, the recommended approach is to map message keys to an array within your React component:

```json filename="en.json"
{
  "CompanyStats": {
    "yearsOfService": {
      "title": "Years of service",
      "value": "34"
    },
    "happyClients": {
      "title": "Happy clients",
      "value": "1.000+"
    },
    "partners": {
      "title": "Products",
      "value": "5.000+"
    }
  }
}
```

```tsx filename="CompanyStats.tsx"
import {useTranslations} from 'next-intl';

function CompanyStats() {
  const t = useTranslations('CompanyStats');
  const items = [
    {
      title: t('yearsOfService.title'),
      value: t('yearsOfService.value')
    },
    {
      title: t('happyClients.title'),
      value: t('happyClients.value')
    },
    {
      title: t('partners.title'),
      value: t('partners.value')
    }
  ];

  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>
          <h2>{item.title}</h2>
          <p>{item.value}</p>
        </li>
      ))}
    </ul>
  );
}
```

This approach ensures that you can use ICU features while also enabling static [validation of messages](/docs/workflows/messages).

<Details id="arrays-varying-amount">
<summary>What if the amount of items varies depending on the locale?</summary>

To dynamically iterate over all keys of a namespace, you can use the [`useMessages`](/docs/usage/configuration#messages) hook to retrieve all messages of a given namespace and extract the keys from there:

```tsx filename="CompanyStats.tsx"
import {useTranslations, useMessages} from 'next-intl';

function CompanyStats() {
  const t = useTranslations('CompanyStats');

  const messages = useMessages();
  const keys = Object.keys(messages.CompanyStats);

  return (
    <ul>
      {keys.map((key) => (
        <li key={key}>
          <h2>{t(`${key}.title`)}</h2>
          <p>{t(`${key}.value`)}</p>
        </li>
      ))}
    </ul>
  );
}
```

</Details>

## Right-to-left languages

Languages such as Arabic, Hebrew and Persian use [right-to-left script](https://en.wikipedia.org/wiki/Right-to-left_script) (often abbreviated as RTL). For these languages, writing begins on the right side of the page and continues to the left.

**Example:**

<div dir="rtl" className="text-right mt-6">

```
النص في اللغة العربية _مثلا_ يُقرأ من اليمين لليسار
```

</div>

In addition to providing translated messages, proper RTL localization requires:

1. Providing the [`dir` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) where relevant
2. Layout mirroring, e.g. by using [CSS logical properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values)
3. Element mirroring, e.g. by customizing icons

**Learn more:**

<CourseVideoBanner
  className="mt-2"
  href="https://learn.next-intl.dev/chapters/09-design-localization/02-right-to-left"
  title="Right-to-left layouts"
/>
